//
//  ThreadSafetyTableController.m
//  ThreadDemo
//
//  Created by 苏沫离 on 2018/7/18.
//  Copyright © 2018年 苏沫离. All rights reserved.
//
#define CellIdentifer @"UITableViewCell"

#import "ThreadSafetyTableController.h"
#include <pthread.h>
#include <sys/time.h>

@interface ThreadSafetyTableController ()

@property (atomic ,strong) NSString *testStrig;
@property (nonatomic ,strong) NSArray<NSString *> *titleArray;

@end

@implementation ThreadSafetyTableController

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifer];
    
    //先监听线程退出的通知，以便知道线程什么时候退出
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadExitNotice:) name:NSThreadWillExitNotification object:nil];

}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)threadExitNotice:(NSNotification *)notification
{
    NSLog(@" threadExitNotice ------------ %@",notification.object);
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.titleArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer forIndexPath:indexPath];
    cell.textLabel.text = self.titleArray[indexPath.row];
    cell.textLabel.numberOfLines = 0;
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    if (indexPath.row == 0)
    {
        [self testDataSafetyMethod];
    }
    else if (indexPath.row == 1)
    {
        [self synchronizedMethod];
    }
    else if (indexPath.row == 2)
    {
        [self testLockMethod];
    }
    else if (indexPath.row == 3)
    {
        [self pthread_mutex_LockMethod];
    }
    else if (indexPath.row == 4)
    {
        [self recursiveDeadLockMethod];
    }
    else if (indexPath.row == 5)
    {
        [self recursiveLockMethod];
    }
    else if (indexPath.row == 6)
    {
        [self pthread_mutex_RecursiveLockMethod];
    }
    else if (indexPath.row == 7)
    {
        [self conditionLockMethod];
    }
    else if (indexPath.row == 8)
    {
        [self conditionMethod];
    }
    else if (indexPath.row == 9)
    {
        [self pthread_mutex_ConditionMethod];
    }
    else if (indexPath.row == 10)
    {
        [self dispatch_semaphoreMethod];
    }
    else if (indexPath.row == 11)
    {
        [self pthread_mutex_SemaphoreMethod];
    }
    else if (indexPath.row == 12)
    {
        [self dispatch_barrier_Method];
    }
    else if (indexPath.row == 13)
    {
        [self pthread_rwlock_Method];
    }
    else if (indexPath.row == 14)
    {
        [self dispatch_once_Method];
    }
    else if (indexPath.row == 15)
    {
        [self pthread_once_Method];
    }


}

#pragma mark -

- (void)testDataSafetyMethod
{
    dispatch_queue_t queue1 = dispatch_queue_create("com.demo.task1", DISPATCH_QUEUE_CONCURRENT);

    __block int length = 0;
    dispatch_apply(6, queue1, ^(size_t index) {
        
        dispatch_async(queue1, ^{
            NSLog(@"length === %d -- %@",++length,NSThread.currentThread);
            [NSThread sleepForTimeInterval:2];//模拟耗时任务
            NSLog(@"length --- %d == %@",--length,NSThread.currentThread);
        });
    });
}

#pragma mark - 互斥锁

#pragma mark  synchronized（互斥锁）
//互斥锁：在任一时刻，只能有一个线程访问该对象。

- (void)synchronizedMethod
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"taskA --- 执行前");
        NSLog(@"taskA === %@",[self getTheString]);
        NSLog(@"taskA --- 执行后");
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskB --- 执行前");
        NSLog(@"taskB === %@",[self getTheString]);
        NSLog(@"taskB --- 执行后");
    });
}

//@synchronized则适用于低频场景如初始化或者紧急修复使用
- (NSString *)getTheString
{
    @synchronized (self)
    {
        NSLog(@"currentThread == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];
        return @"*****************************";
    }
}

#pragma mark  NSLock（互斥锁）

/*
 NSLock 有三种方法来执行加锁操作，那么这三个方法有何区别呢?
 我们使用一段代码，根据不同时间下的加锁操作，分别测试下 NSLock 的三种加锁方法的特性：
 */
- (void)testLockMethod
{
    NSLog(@"开始处理 ---- %@",NSThread.currentThread);
    __block int length = 0;
    NSLock *lock = [[NSLock alloc] init];//初始化一个 NSLock 实例
    lock.name = @"task.lock";
    //将 taskA 放到一个并发队列中异步执行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskA === 开始尝试加锁");
        [lock lock];//给 taskA 加锁
        NSLog(@"taskA === length === %d -- %@",++length,NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        NSLog(@"taskA === length --- %d == %@",--length,NSThread.currentThread);
        [lock unlock];//taskA 完成后解锁
        NSLog(@"taskA --- 已经解锁");
    });
    
    //将 taskB 放到一个并发队列中异步执行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskB === 开始尝试加锁");
        [lock lock];//给 taskB 加锁
        NSLog(@"taskB === length === %d -- %@",++length,NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];
        NSLog(@"taskB === length --- %d == %@",--length,NSThread.currentThread);
        [lock unlock];//taskB 完成后解锁
        NSLog(@"taskB --- 已经解锁");
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        //tryLock 并不会阻塞线程。[lock tryLock] 能加锁返回 YES，不能加锁返回 NO，然后都会执行后续代码。
        NSLog(@"taskC === 开始尝试加锁");
        if ([lock tryLock])
        {
            NSLog(@"taskC === length === %d -- %@",++length,NSThread.currentThread);
            [NSThread sleepForTimeInterval:2];//模拟耗时任务
            NSLog(@"taskC === length --- %d == %@",--length,NSThread.currentThread);
            [lock unlock];//taskC 完成后解锁
            NSLog(@"taskC --- 已经解锁");
        }
        else
        {
            NSLog(@"加锁失败 ：taskC === length --- %d == %@",length,NSThread.currentThread);
        }
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        //在所指定 Date 之前尝试加锁，会阻塞线程，如果在指定时间之前都不能加锁，则返回 NO，指定时间之前能加锁，则返回 YES。
        //taskD 尝试在 3s 之后加锁，
        NSLog(@"taskD === 开始尝试加锁");
        if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:3]]){
            NSLog(@"taskD === length === %d -- %@",++length,NSThread.currentThread);
            [NSThread sleepForTimeInterval:2];
            NSLog(@"taskD === length --- %d == %@",--length,NSThread.currentThread);
            [lock unlock];
            NSLog(@"taskD --- 已经解锁");
        }
        else
        {
            NSLog(@"加锁失败 ：taskD === length --- %d == %@",length,NSThread.currentThread);
        }
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        //taskE 尝试在 10s 之后加锁 ，等待前面所有任务全部执行完毕，确保 length 资源没有被加锁
        NSLog(@"taskE === 开始尝试加锁");
        if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]){
            NSLog(@"taskE === length === %d -- %@",++length,NSThread.currentThread);
            [NSThread sleepForTimeInterval:2];
            NSLog(@"taskE === length --- %d == %@",--length,NSThread.currentThread);
            [lock unlock];
            NSLog(@"taskE --- 已经解锁");
        }
        else
        {
            NSLog(@"加锁失败 ：taskE === length --- %d == %@",length,NSThread.currentThread);
        }
    });
    
    //dispatch_after函数并不是在指定时间之后才开始执行处理，而是在指定时间之后将任务追加到队列中。
    //延迟 15s 执行 ，等待前面所有任务全部执行完毕，确保 length 资源没有被加锁
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskF === 开始尝试加锁");
        if ([lock tryLock])
        {
            NSLog(@"taskF === length === %d -- %@",++length,NSThread.currentThread);
            [NSThread sleepForTimeInterval:2];
            NSLog(@"taskF === length --- %d == %@",--length,NSThread.currentThread);
            [lock unlock];
            NSLog(@"taskF --- 已经解锁");
        }
        else
        {
            NSLog(@"加锁失败 ：taskF === length --- %d == %@",length,NSThread.currentThread);
        }
    });
    
    NSLog(@"结束处理 ---- %@",NSThread.currentThread);
}

#pragma mark pthread_mutex（互斥锁）

/*
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
 */
- (void)pthread_mutex_LockMethod
{
    NSLog(@"开始处理 ---- %@",NSThread.currentThread);
    dispatch_group_t group = dispatch_group_create();
    __block pthread_mutex_t pthreadLock = PTHREAD_MUTEX_INITIALIZER;//静态创建一个互斥锁
    __block int length = 0;
    
    //将 taskA 放到一个并发队列中异步执行
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskA === 开始尝试加锁");
        pthread_mutex_lock(&pthreadLock);//给 taskA 加锁
        NSLog(@"taskA === length === %d -- %@",++length,NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        NSLog(@"taskA === length --- %d == %@",--length,NSThread.currentThread);
        pthread_mutex_unlock(&pthreadLock);//taskA 完成后解锁
        NSLog(@"taskA --- 已经解锁");
    });
    
    //将 taskB 放到一个并发队列中异步执行
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskB === 开始尝试加锁");
        pthread_mutex_lock(&pthreadLock);//给 taskB 加锁
        NSLog(@"taskB === length === %d -- %@",++length,NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];
        NSLog(@"taskB === length --- %d == %@",--length,NSThread.currentThread);
        pthread_mutex_unlock(&pthreadLock);//taskB 完成后解锁
        NSLog(@"taskB --- 已经解锁");
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskC === 开始尝试加锁");
        if (pthread_mutex_trylock(&pthreadLock) == 0)
        {
            NSLog(@"taskC === length === %d -- %@",++length,NSThread.currentThread);
            [NSThread sleepForTimeInterval:2];//模拟耗时任务
            NSLog(@"taskC === length --- %d == %@",--length,NSThread.currentThread);
            pthread_mutex_unlock(&pthreadLock);//taskC 完成后解锁
            NSLog(@"taskC --- 已经解锁");
        }
        else
        {
            NSLog(@"加锁失败 ：taskC === length --- %d == %@",length,NSThread.currentThread);
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSThread sleepForTimeInterval:6];//堵塞当前线程6s，等待前面所有任务全部执行完毕，确保 length 资源没有被加锁
        NSLog(@"taskD === 开始尝试加锁");
        if (pthread_mutex_trylock(&pthreadLock) == 0)
        {
            NSLog(@"taskD === length === %d -- %@",++length,NSThread.currentThread);
            [NSThread sleepForTimeInterval:2];//模拟耗时任务
            NSLog(@"taskD === length --- %d == %@",--length,NSThread.currentThread);
            pthread_mutex_unlock(&pthreadLock);//taskC 完成后解锁
            NSLog(@"taskD --- 已经解锁");
        }
        else
        {
            NSLog(@"加锁失败 ：taskD === length --- %d == %@",length,NSThread.currentThread);
        }
    });
    
    //延迟 10 执行 ，等待前面所有任务全部执行完毕，确保 length 资源没有被加锁
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"taskE === 开始尝试加锁");
            if (pthread_mutex_trylock(&pthreadLock) == 0)
            {
                NSLog(@"taskE === length === %d -- %@",++length,NSThread.currentThread);
                [NSThread sleepForTimeInterval:2];//模拟耗时任务
                NSLog(@"taskE === length --- %d == %@",--length,NSThread.currentThread);
                pthread_mutex_unlock(&pthreadLock);//taskC 完成后解锁
                NSLog(@"taskE --- 已经解锁");
            }
            else
            {
                NSLog(@"加锁失败 ：taskE === length --- %d == %@",length,NSThread.currentThread);
            }
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"group中所有任务完成 ---- %@",NSThread.currentThread);
        pthread_mutex_destroy(&pthreadLock);
    });
}

#pragma mark - 递归锁
#pragma mark NSLock(递归死锁)

/*
 我们不妨先看下使用 NSLock 在递归中运行的效果
 死锁
 */
- (void)recursiveDeadLockMethod
{
    NSLog(@"开始处理 ------------ %@",NSThread.currentThread);
    NSLock *lock = [[NSLock alloc] init];
    static void (^recursiveTestBlock)(int length);
    //使用 NSThread 开辟一条新线程执行递归操作处理taskA
    [NSThread detachNewThreadWithBlock:^{
        recursiveTestBlock = ^(int length)
        {
            NSLog(@"开始加锁 ： taskA === length === %d -- %@",length,NSThread.currentThread);
            [lock lock];
            if (length > 0)
            {
                [NSThread sleepForTimeInterval:2];//模拟耗时任务
                recursiveTestBlock(--length);
            }
            [lock unlock];
            NSLog(@"已经解锁 ： taskA --- length --- %d == %@",length,NSThread.currentThread);
        };
        recursiveTestBlock(3);
    }];
    
    //使用 NSThread 开辟一条新线程执行递归操作处理taskB
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"开始加锁 ： taskB === %@",NSThread.currentThread);
        [lock lock];
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        [lock unlock];
        NSLog(@"已经解锁 ： taskB === %@",NSThread.currentThread);
    }];
    
    NSLog(@"结束处理 ------------ %@",NSThread.currentThread);
}

#pragma mark NSRecursiveLock(递归锁)
//不同线程请求，则在加锁线程解锁时重新竞争?
- (void)recursiveLockMethod
{
    NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
    static void (^recursiveTestBlock)(int length);
    
    //使用 NSThread 开辟一条新线程执行递归操作处理taskA
    [NSThread detachNewThreadWithBlock:^{
        
        recursiveTestBlock = ^(int length)
        {
            NSLog(@"开始加锁 ： taskA === length === %d -- %@",length,NSThread.currentThread);
            [recursiveLock lock];
            if (length > 0)
            {
                [NSThread sleepForTimeInterval:2];
                recursiveTestBlock(--length);
            }
            [recursiveLock unlock];
            NSLog(@"已经解锁 ： taskA --- length --- %d == %@",length,NSThread.currentThread);
        };
        recursiveTestBlock(3);
    }];
    //使用 NSThread 开辟一条新线程处理taskB
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"开始加锁 ： taskB === %@",NSThread.currentThread);
        [recursiveLock lock];
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        [recursiveLock unlock];
        NSLog(@"已经解锁 ： taskB === %@",NSThread.currentThread);
    }];
}

#pragma mark pthread_mutex（递归锁）

- (void)pthread_mutex_RecursiveLockMethod
{
    // PTHREAD_MUTEX_RECURSIVE_NP：嵌套锁，允许同一个线程对同一个锁成功获得多次，并通过多次unlock解锁。如果是不同线程请求，则在加锁线程解锁时重新竞争。
    __block pthread_mutex_t pthreadLock;
    pthread_mutexattr_t pthreadMutexattr;
    pthread_mutexattr_init(&pthreadMutexattr);
    pthread_mutexattr_settype(&pthreadMutexattr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&pthreadLock, &pthreadMutexattr);//初始化
    
    static void (^recursiveTestBlock)(int length);
    
    //使用 NSThread 开辟一条新线程执行递归操作处理taskA
    [NSThread detachNewThreadWithBlock:^{
        
        recursiveTestBlock = ^(int length)
        {
            NSLog(@"开始加锁 ： taskA === length === %d -- %@",length,NSThread.currentThread);
            pthread_mutex_lock(&pthreadLock);//上锁
            if (length > 0)
            {
                [NSThread sleepForTimeInterval:2];
                recursiveTestBlock(--length);
            }
            pthread_mutex_unlock(&pthreadLock);//解锁
            NSLog(@"已经解锁 ： taskA --- length --- %d == %@",length,NSThread.currentThread);
        };
        recursiveTestBlock(3);
    }];
    //使用 NSThread 开辟一条新线程处理taskB
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"开始加锁 ： taskB === %@",NSThread.currentThread);
        pthread_mutex_lock(&pthreadLock);//上锁
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        pthread_mutex_unlock(&pthreadLock);//解锁
        NSLog(@"已经解锁 ： taskB === %@",NSThread.currentThread);
    }];
}

#pragma mark - 条件锁

#pragma mark NSConditionLock（条件锁）

- (void)conditionLockMethod
{
    NSLog(@"开始处理 ------------ %@",NSThread.currentThread);
    
    __block int length = 0;
    
    //初始化一个条件锁，设置解锁条件为 1
    NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:1];
    
    //将 taskA 放到一个并发队列中异步执行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskA === 开始尝试加锁");
        [conditionLock lock];//给 taskA 加锁
        NSLog(@"taskA === length === %d -- %@",++length,NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        NSLog(@"taskA === length --- %d == %@",--length,NSThread.currentThread);
        [conditionLock unlock];//taskA 完成后解锁
        NSLog(@"taskA --- 已经解锁");
    });
    
    //将 taskB 放到一个并发队列中异步执行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskB === 开始尝试加锁");
        if ([conditionLock tryLockWhenCondition:1])//不会阻塞线程
        {
            NSLog(@"taskB === length === %d -- %@",++length,NSThread.currentThread);
            [NSThread sleepForTimeInterval:2];//模拟耗时任务
            NSLog(@"taskB === length --- %d == %@",--length,NSThread.currentThread);
            [conditionLock unlock];//taskB 完成后解锁
            NSLog(@"taskB --- 已经解锁");
        }
        else
        {
            NSLog(@"加锁失败 ：taskB === length --- %d == %@",length,NSThread.currentThread);
        }
    });

    //将 taskC 放到一个并发队列中异步执行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskC === 开始尝试加锁");
        if ([conditionLock lockWhenCondition:1 beforeDate:[NSDate dateWithTimeIntervalSinceNow:4]])
        {
            NSLog(@"taskC === length === %d -- %@",++length,NSThread.currentThread);
            [NSThread sleepForTimeInterval:2];//模拟耗时任务
            NSLog(@"taskC === length --- %d == %@",--length,NSThread.currentThread);
            [conditionLock unlockWithCondition:5];//taskC 完成后解锁
            NSLog(@"taskC --- 已经解锁");
        }
        else
        {
            NSLog(@"加锁失败 ：taskC === length --- %d == %@",length,NSThread.currentThread);
        }
    });
    
    
    //将 taskD 放到一个并发队列中异步执行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"taskD === 开始尝试加锁");
        [conditionLock lockWhenCondition:3];
        NSLog(@"taskD === length === %d -- %@",++length,NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        NSLog(@"taskD === length --- %d == %@",--length,NSThread.currentThread);
        [conditionLock unlockWithCondition:1];//taskE 完成后解锁
        NSLog(@"taskD --- 已经解锁");
    });

    
    //延迟 10s 之后执行下述任务，等待前面所有任务全部执行完毕，确保 length 资源没有被加锁
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       
        //将 taskE 放到一个并发队列中异步执行
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"taskE === 开始尝试加锁");
            if ([conditionLock tryLockWhenCondition:3])
            {
                NSLog(@"taskE === length === %d -- %@",++length,NSThread.currentThread);
                [NSThread sleepForTimeInterval:2];//模拟耗时任务
                NSLog(@"taskE === length --- %d == %@",--length,NSThread.currentThread);
                [conditionLock unlockWithCondition:1];//taskD 完成后解锁
                NSLog(@"taskE --- 已经解锁");
            }
            else
            {
                NSLog(@"加锁失败 ：taskE === length --- %d == %@",length,NSThread.currentThread);
            }
        });

        
        //将 taskF 放到一个并发队列中异步执行
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"taskF === 开始尝试加锁");
            if ([conditionLock tryLockWhenCondition:5])
            {
                NSLog(@"taskF === length === %d -- %@",++length,NSThread.currentThread);
                [NSThread sleepForTimeInterval:2];//模拟耗时任务
                NSLog(@"taskF === length --- %d == %@",--length,NSThread.currentThread);
                [conditionLock unlockWithCondition:3];//taskF 完成后解锁
                NSLog(@"taskF --- 已经解锁");
            }
            else
            {
                NSLog(@"加锁失败 ：taskF === length --- %d == %@",length,NSThread.currentThread);
            }
        });
    });
    NSLog(@"结束处理 ------------ %@",NSThread.currentThread);
}



#pragma mark NSCondition（条件锁）

- (void)conditionMethod
{
    NSCondition *condition = [[NSCondition alloc] init];
    NSMutableArray *array = [[NSMutableArray alloc] init];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        
        NSLog(@"开始加锁 ： taskA === %@",NSThread.currentThread);
        [condition lock];//锁定条件对象。
        [array addObject:@1];
        
        //wait接到signal后，并不意味着条件的值一定发生了变化，必须重新检查条件的值。最好的测试方法是循环调用：
        while ([array containsObject:@5] == NO)//测试是否可以安全的履行接下来的任务。
        {
            NSLog(@"执行wait： taskA === %@",NSThread.currentThread);
            //在调用 wait 方法前，必须调用 lock 给当前线程上锁
            //当一个线程执行 wait 时，NSCondition 对象 解开锁 并在此处阻塞当前线程
            //当 NSCondition 发送 signal 或者 broadcast 信号时唤醒线程
            [condition wait];//如果布尔值是假的，调用条件对象的 wait 方法来阻塞线程，不会继续执行，只有等到  signal 或 broadcast 信号，才会接着执行
            NSLog(@"接signal： taskA === %@",NSThread.currentThread);
        }
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        [condition broadcast];
        [condition unlock];//当任务完成后，解锁条件对象
        NSLog(@"已经解锁 ： taskA === %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"开始加锁 ： taskB === %@",NSThread.currentThread);
        [condition lock];

        while ([array containsObject:@5] == NO)
        {
            NSLog(@"执行wait： taskB === %@",NSThread.currentThread);
            [condition wait];
            NSLog(@"接signal： taskB === %@",NSThread.currentThread);
        }
        [array addObject:@2];
        [condition unlock];
        NSLog(@"已经解锁 ： taskB === %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"开始加锁 ： taskC === %@",NSThread.currentThread);
        [condition lock];
        
        while ([array containsObject:@5] == NO)
        {
            NSLog(@"执行wait： taskC === %@",NSThread.currentThread);
            [condition wait];
            NSLog(@"接signal： taskC === %@",NSThread.currentThread);
        }
        [array addObject:@4];
        [condition unlock];
        NSLog(@"已经解锁 ： taskC === %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"开始加锁 ： taskD === %@",NSThread.currentThread);
        [condition lock];
        
        while ([array containsObject:@4] == NO)
        {
            NSLog(@"执行wait： taskD === %@",NSThread.currentThread);
            if ([condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]])
            {
                NSLog(@"接signal： taskD === %@",NSThread.currentThread);
            }
            else
            {
                NSLog(@"taskD ---- 指定时间内也没有收到 信号");
            }
        }
        [array addObject:@3];
        [condition unlock];
        NSLog(@"已经解锁 ： taskD === %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"开始加锁 ： taskF === %@",NSThread.currentThread);
        [condition lock];
        [array addObject:@5];
        [condition signal];
        NSLog(@"发送信号 ： taskF === %@",NSThread.currentThread);
        [condition unlock];
        NSLog(@"已经解锁 ： taskF === %@",NSThread.currentThread);
    });
}

/*
 -(void)wait; 会使线程一直处于休眠状态，直到收到调用 -(void)signal; 发出的signal为止；
 -(BOOL)waitUntilDate:(NSDate *)limit; 在使线程睡眠的同时会设置睡眠的终止时间，如果在终止时间前收到了signal就会唤醒线程；当到达终止时间的时候，即使没有收到signal，也会直接唤醒线程，而不会像 -(void)wait; 那样一直睡眠下去
 -(void)signal; signal 只是一个信号量，只能唤醒一个等待的线程，想唤醒多个就得多次调用；如果没有等待的线程，则这个方法不起作用；
 -(void)broadcast;  可以唤醒所有在等待的线程。如果没有等待的线程，这个方法没有作用。
 */

#pragma mark pthread_mutex（条件锁）
/*
 条件变量是利用线程间共享的全局变量进行同步的一种机制，主要包括两个动作：一个线程等待"条件变量的条件成立"而挂起；另一个线程使"条件成立"（给出条件成立信号）。为了防止竞争，条件变量的使用总是和一个互斥锁结合在一起。
 */
- (void)pthread_mutex_ConditionMethod
{
    __block pthread_mutex_t condMutexLock;
    __block pthread_cond_t pthreadCondition;//条件变量
    pthread_mutex_init(&condMutexLock, NULL);//初始化一个互斥锁
    pthread_cond_init(&pthreadCondition, NULL);//初始化一个条件变量
    
    NSMutableArray *array = [[NSMutableArray alloc] init];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        
        NSLog(@"开始加锁 ： taskA === %@",NSThread.currentThread);
        pthread_mutex_lock(&condMutexLock);//上锁
        [array addObject:@1];
        
        //wait接到signal后，并不意味着条件的值一定发生了变化，必须重新检查条件的值。最好的测试方法是循环调用：
        while ([array containsObject:@5] == NO)//测试是否可以安全的履行接下来的任务。
        {
            NSLog(@"执行wait： taskA === %@",NSThread.currentThread);
            pthread_cond_wait(&pthreadCondition, &condMutexLock);
            NSLog(@"接signal： taskA === %@",NSThread.currentThread);
        }
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        pthread_cond_broadcast(&pthreadCondition);
        pthread_mutex_unlock(&condMutexLock);//解锁
        NSLog(@"已经解锁 ： taskA === %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"开始加锁 ： taskB === %@",NSThread.currentThread);
        pthread_mutex_lock(&condMutexLock);//上锁

        while ([array containsObject:@5] == NO)
        {
            NSLog(@"执行wait： taskB === %@",NSThread.currentThread);
            pthread_cond_wait(&pthreadCondition, &condMutexLock);
            NSLog(@"接signal： taskB === %@",NSThread.currentThread);
        }
        [array addObject:@2];
        pthread_mutex_unlock(&condMutexLock);//解锁
        NSLog(@"已经解锁 ： taskB === %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"开始加锁 ： taskC === %@",NSThread.currentThread);
        pthread_mutex_lock(&condMutexLock);//上锁

        while ([array containsObject:@5] == NO)
        {
            NSLog(@"执行wait： taskC === %@",NSThread.currentThread);
            pthread_cond_wait(&pthreadCondition, &condMutexLock);
            NSLog(@"接signal： taskC === %@",NSThread.currentThread);
        }
        [array addObject:@4];
        pthread_mutex_unlock(&condMutexLock);//解锁
        NSLog(@"已经解锁 ： taskC === %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"开始加锁 ： taskD === %@",NSThread.currentThread);
        pthread_mutex_lock(&condMutexLock);//上锁

        while ([array containsObject:@4] == NO)
        {
            NSLog(@"执行wait： taskD === %@",NSThread.currentThread);
            struct timespec abstime;
            struct timeval now;
            long timeout_s = 1; // 等待 1s
            gettimeofday(&now, NULL);
            long nsec = now.tv_usec * 1000 + timeout_s * 1000000;
            abstime.tv_sec=now.tv_sec + nsec / 1000000000 + timeout_s;
            abstime.tv_nsec=nsec % 1000000000;
            if (pthread_cond_timedwait(&pthreadCondition, &condMutexLock, &abstime) == 0)
            {
                NSLog(@"接signal： taskD === %@",NSThread.currentThread);
            }
            else
            {
                NSLog(@"taskD ---- 指定时间内也没有收到 信号");
            }
        }
        [array addObject:@3];
        pthread_mutex_unlock(&condMutexLock);//解锁
        NSLog(@"已经解锁 ： taskD === %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"开始加锁 ： taskF === %@",NSThread.currentThread);
        pthread_mutex_lock(&condMutexLock);//上锁
        [array addObject:@5];
        pthread_cond_signal(&pthreadCondition);
        NSLog(@"发送信号 ： taskF === %@",NSThread.currentThread);
        pthread_mutex_unlock(&condMutexLock);//解锁
        NSLog(@"已经解锁 ： taskF === %@",NSThread.currentThread);
    });
}

/*
等待条件有两种方式：无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait()，其中计时等待方式如果在给定时刻前条件没有满足，则返回ETIMEOUT，结束等待，其中abstime以与time()系统调用相同意义的绝对时间形式出现，0表示格林尼治时间1970年1月1日0时0分0秒。
无论哪种等待方式，都必须和一个互斥锁配合，以防止多个线程同时请求pthread_cond_wait()（或pthread_cond_timedwait()，下同）的竞争条件（Race   Condition）。mutex互斥锁必须是普通锁（PTHREAD_MUTEX_TIMED_NP）或者适应锁（PTHREAD_MUTEX_ADAPTIVE_NP），且在调用pthread_cond_wait()前必须由本线程加锁（pthread_mutex_lock()），而在更新条件等待队列以前，mutex保持锁定状态，并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前，mutex将被重新加锁，以与进入pthread_cond_wait()前的加锁动作对应。

 */

#pragma mark - 信号量

#pragma mark dispatch_semaphore（信号量）
/*
 dispatch_semaphore 是 GCD 用来同步的一种方式，与他相关的只有三个函数，一个是创建信号量，一个是等待信号，一个是发送信号。
 
 dispatch_semaphore_create(long value);//创建信号量
 dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);//等待信号
 dispatch_semaphore_signal(dispatch_semaphore_t dsema);//发送信号
 */

/*
 信号量(Semaphore)，有时被称为信号灯，是在多线程环境下使用的一种设施，是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前，线程必须获取一个信号量；一旦该关键代码段完成了，那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量
 

 dispatch_semaphore 和 NSCondition 类似，都是一种基于信号的同步方式，但 NSCondition 信号只能发送，不能保存（如果没有线程在等待，则发送的信号会失效）。而 dispatch_semaphore 能保存发送的信号。dispatch_semaphore 的核心是 dispatch_semaphore_t 类型的信号量。
 
 
 GCD 不像 NSOperation 那样有直接提供线程数量控制方法，但是我们利用 GCD 的 dispatch_group_t 和 dispatch_semaphore_t 功能，可以做到控制线程数量，并且在所有任务执行完成之后得到通知。
 */

- (void)dispatch_semaphoreMethod
{
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    
    NSLog(@"-------- 开始处理 -------");
    
    dispatch_apply(6, queue, ^(size_t i) {
        
        long single = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        // 只有当信号量大于 0 的时候，线程将信号量减 1，程序向下执行
        // 否则线程会阻塞并且一直等待，直到信号量大于 0
        dispatch_group_async(group, queue, ^{
            NSLog(@"开始执行第 %zu 次任务 == single : %ld----- %@",i,single,NSThread.currentThread);
            [NSThread sleepForTimeInterval:(i % 2 == 0 ? 2 : 3)];//模拟耗时任务
            long value = dispatch_semaphore_signal(semaphore);// 当线程任务执行完成之后，发送一个信号，增加信号量。
            NSLog(@"结束执行第 %zu 次任务 == single : %ld----- %@",i,value,NSThread.currentThread);
        });
    });
    
    //既控制了线程数量，也在执行任务完成之后得到了通知。
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务结束 ------ %@",NSThread.currentThread);
    });
}

#pragma mark pthread_mutex（信号量）
/*
 信号量(Semaphore)，有时被称为信号灯，是在多线程环境下使用的一种设施，是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前，线程必须获取一个信号量；一旦该关键代码段完成了，那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量
 */
- (void)pthread_mutex_SemaphoreMethod
{
    __block pthread_mutex_t pthreadLock;
    pthread_mutexattr_t pthreadMutexattr;
    pthread_mutexattr_init(&pthreadMutexattr);
    pthread_mutexattr_settype(&pthreadMutexattr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&pthreadLock, &pthreadMutexattr);//初始化
    
    __block pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//使用静态方法
    __block int limitCount = 2;
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    
    NSLog(@"-------- 开始处理 -------");
    
    dispatch_apply(6, queue, ^(size_t i) {
        
        pthread_mutex_lock(&pthreadLock);
        
        limitCount --;
        while (limitCount < 0)
        {
            NSLog(@"执行第 %zu 次等待",i);
            pthread_cond_wait(&cond, &pthreadLock);
        }

        dispatch_group_async(group, queue, ^{
            NSLog(@"开始执行第 %zu 次任务 == single : %d----- %@",i,limitCount,NSThread.currentThread);
            [NSThread sleepForTimeInterval:(i % 2 == 0 ? 2 : 3)];//模拟耗时任务
            ++limitCount;
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&pthreadLock);
            NSLog(@"结束执行第 %zu 次任务 == single : %d----- %@",i,limitCount,NSThread.currentThread);
        });
    });
    
    //既控制了线程数量，也在执行任务完成之后得到了通知。
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务结束 ------ %@",NSThread.currentThread);
    });
    
}

#pragma mark - 读写锁
/*
 读写锁实际是一种特殊的自旋锁，它把对共享资源的访问者划分成读者和写者，读者只对共享资源进行读访问，写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言，能提高并发性，因为在多处理器系统中，它允许同时有多个读者来访问共享资源，最大可能的读者数为实际的逻辑CPU数。写者是排他性的，一个读写锁同时只能有一个写者或多个读者（与CPU数相关），但不能同时既有读者又有写者。
 */
#pragma mark  dispatch_barrier(读写锁)

- (void)dispatch_barrier_Method1
{
    NSMutableArray *array = [NSMutableArray array];
    dispatch_queue_t queue = dispatch_queue_create("com.demo.barrier", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"开始执行写入操作");
    dispatch_async(queue, ^{
        NSLog(@"开始执行taskA === %@",NSThread.currentThread);
        for (int i = 0; i < 100; i ++)
        {
            [array addObject:@(i)];
        }
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行taskA === %@",NSThread.currentThread);
    });
    
    //DISPATCH_BLOCK_BARRIER 保证代码块用于原子性，代码块的代码未执行结束前，下一次调用将进入一个FIFO的等待队列，等待本次代码块执行结束，使用较为安全
    dispatch_block_t taskC = dispatch_block_create(DISPATCH_BLOCK_BARRIER, ^{
        NSLog(@"开始执行taskC === %@",NSThread.currentThread);
        NSLog(@"taskC读取数据---- %@",array[189]);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行taskC === %@",NSThread.currentThread);
    });
    dispatch_block_t taskD = dispatch_block_create(DISPATCH_BLOCK_BARRIER, ^{
        NSLog(@"开始执行taskD === %@",NSThread.currentThread);
        NSLog(@"taskD读取数据---- %@",array[10]);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行taskD === %@",NSThread.currentThread);
    });
    
    NSLog(@"开始执行判定操作");
    dispatch_barrier_async(queue, ^{
        NSLog(@"开始执行taskB === %@",NSThread.currentThread);
        if (array.count < 100){
            //dispatch_block_cancel() 取消执行某个block，只有当block还未执行前执行cancel有效，block正在执行无法取消.
            dispatch_block_cancel(taskC);
            dispatch_block_cancel(taskD);
        }else if (array.count < 200){
            dispatch_block_cancel(taskC);
        }
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行taskB === %@",NSThread.currentThread);
    });
    
    NSLog(@"开始执行读取操作");
    dispatch_async(queue, taskC);
    dispatch_async(queue, taskD);
    NSLog(@"结束执行读写操作");
}

- (void)dispatch_barrier_Method
{
    NSMutableArray *array = [NSMutableArray array];
    dispatch_queue_t queue = dispatch_queue_create("com.demo.barrier", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"开始执行写入操作");
    dispatch_async(queue, ^{
        NSLog(@"开始执行taskA === %@",NSThread.currentThread);
        for (int i = 0; i < 100; i ++)
        {
            [array addObject:@(i)];
        }
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行taskA === %@",NSThread.currentThread);
    });
    
    //DISPATCH_BLOCK_BARRIER 保证代码块用于原子性，代码块的代码未执行结束前，下一次调用将进入一个FIFO的等待队列，等待本次代码块执行结束，使用较为安全
    dispatch_block_t taskC = dispatch_block_create(DISPATCH_BLOCK_BARRIER, ^{
        NSLog(@"开始执行taskC === %@",NSThread.currentThread);
        NSLog(@"taskC读取数据---- %@",array[189]);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行taskC === %@",NSThread.currentThread);
    });
    dispatch_block_t taskD = dispatch_block_create(DISPATCH_BLOCK_BARRIER, ^{
        NSLog(@"开始执行taskD === %@",NSThread.currentThread);
        NSLog(@"taskD读取数据---- %@",array[10]);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行taskD === %@",NSThread.currentThread);
    });

    NSLog(@"开始执行判定操作");
    dispatch_barrier_sync(queue, ^{
        NSLog(@"开始执行taskB === %@",NSThread.currentThread);
        if (array.count < 100){
            //dispatch_block_cancel() 取消执行某个block，只有当block还未执行前执行cancel有效，block正在执行无法取消.
            dispatch_block_cancel(taskC);
            dispatch_block_cancel(taskD);
        }else if (array.count < 200){
            dispatch_block_cancel(taskC);
        }
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行taskB === %@",NSThread.currentThread);
    });
    NSLog(@"开始执行读取操作");
    dispatch_async(queue, taskC);
    dispatch_async(queue, taskD);
    NSLog(@"结束执行读写操作");
}

/*
 dispatch_barrier_sync 与 dispatch_barrier_async
 相同点：都是将 dispatch_queue_t 一分为2，前面的任务执行完，才执行 自己 block 中的任务
 不同点：
 （1）、dispatch_barrier_sync 与 dispatch_sync 一样，将指定的Block同步追加到指定的Dispatch Queue中，
 dispatch_barrier_sync 函数 block中的代码与 dispatch_barrier_sync 函数上下文的代码处于同一线程
 （2）、dispatch_barrier_async 与 dispatch_async 一样，
 dispatch_barrier_sync 函数 block中的代码与 dispatch_barrier_sync 函数上下文的代码处于不同线程
 */

#pragma mark  pthread_rwlock_t(读写锁)
/*
 读写锁 pthread_rwlock_t 是用来解决读写操作问题的，读取数据操作可以共享，写入数据操作是排他的，读取数据可以有多个在读，，写入数据只有唯一个在写，同时写入数据的时候不允许读取数据。它具有强读者同步和强写者同步两种形式:
 强写者同步：当所有写者都写完之后，才能进行读操作，读者需要最新的信息，一些事实性较高的系统可能会用到该所，比如定票之类的。
 强读者同步：当写者没有进行写操作，读者就可以访问；
 */

- (void)pthread_rwlock_Method
{
    __block pthread_rwlock_t rwlock;
    pthread_rwlock_init(&rwlock, NULL);
    NSMutableArray *array = [NSMutableArray array];
    dispatch_queue_t queue = dispatch_queue_create("com.demo.barrier", DISPATCH_QUEUE_CONCURRENT);

    //DISPATCH_BLOCK_DETACHED 不考虑线程安全
    dispatch_block_t writeTask = dispatch_block_create(DISPATCH_BLOCK_BARRIER, ^{
        pthread_rwlock_wrlock(&rwlock);
        NSInteger index = array.count;
        NSLog(@"开始执行writeTask:%ld === %@",index,NSThread.currentThread);
        [array addObject:@(index)];
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行writeTask:%ld === %@",index,NSThread.currentThread);
        pthread_rwlock_unlock(&rwlock);
    });

    NSLog(@"开始执行写入操作");
    for (int i = 0; i < 3; i++) {
        dispatch_async(queue, writeTask);
    }
    
    NSLog(@"开始执行读取操作");
    for (NSInteger index = 0; index < 3; index++) {
        
        dispatch_async(queue, ^{
            pthread_rwlock_rdlock(&rwlock);
            NSLog(@"开始执行readTask:%ld === %@",index,NSThread.currentThread);
            NSLog(@"读取数据:%ld---- %@",index,array[index]);
            [NSThread sleepForTimeInterval:3];//模拟耗时任务
            NSLog(@"结束执行readTask:%ld === %@",index,NSThread.currentThread);
            pthread_rwlock_unlock(&rwlock);
        });
    }
    NSLog(@"结束执行读写操作");
}
/*
 通过“开始执行写入操作”、“开始执行读取操作”、“结束执行读写操作”的打印时间，可以了解到pthread_rwlock_t()不会阻塞当前线程；
 通过writeTask可以看到，pthread_rwlock_wrlock()写入数据操作是排他的,只有唯一个在写，同时写入数据的时候不允许读取数据;
 通过readTask可以看到，pthread_rwlock_rdlock()读取数据可以有多个在读
 */

/*
 //初始化一个读写锁
 int pthread_rwlock_init(pthread_rwlock_t * __restrict, const pthread_rwlockattr_t * _Nullable __restrict)
 //释放指定读写锁
 int pthread_rwlock_destroy(pthread_rwlock_t * );
 
 //读写锁 锁定 写入数据操作,会堵塞线程
 int pthread_rwlock_wrlock(pthread_rwlock_t *);
 
 //读写锁 锁定 写入数据操作,不会堵塞线程
 int pthread_rwlock_trywrlock(pthread_rwlock_t *);
 
 //读写锁 锁定 读取数据操作,会堵塞线程
 int pthread_rwlock_rdlock(pthread_rwlock_t *);
 //读写锁 锁定 读取数据操作,不会堵塞线程
 int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
 
 //解锁读写锁
 int pthread_rwlock_unlock(pthread_rwlock_t *);
 */

/*
 互斥锁与读写锁的区别：
 
 当访问临界区资源时（访问的含义包括所有的操作：读和写），需要上互斥锁；
 
 当对数据（互斥锁中的临界区资源）进行读取时，需要上读取锁，当对数据进行写入时，需要上写入锁。
 
 读写锁的优点：
 
 对于读数据比修改数据频繁的应用，用读写锁代替互斥锁可以提高效率。因为使用互斥锁时，即使是读出数据（相当于操作临界区资源）都要上互斥锁，而采用读写锁，则可以在任一时刻允许多个读出者存在，提高了更高的并发度，同时在某个写入者修改数据期间保护该数据，以免任何其它读出者或写入者的干扰。
 */

#pragma mark - Once 原子操作

#pragma mark - dispatch_once_t

- (void)dispatch_once_Method
{
    static id shareInstance;
    static dispatch_once_t onceToken;
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(3, queue, ^(size_t index) {
        NSLog(@"开始执行：%zu ====== %@",index,NSThread.currentThread);
        dispatch_once(&onceToken, ^{
            if (!shareInstance) {
                shareInstance = [[NSObject alloc] init];
                [NSThread sleepForTimeInterval:3];
                NSLog(@"dispatch_once 执行内部 ====== %@",NSThread.currentThread);
                
            }
        });
        NSLog(@"结束执行：%zu ====== %@",index,NSThread.currentThread);
    });
}
/*
 如果在一个线程调用dispatch_once()函数时，另外的线程调用dispatch_once，则调用线程等待，直到首次调用dispatch_once()的线程返回。
 */


void pthread_once_Function(void) {
    static id shareInstance;
    shareInstance = [[NSObject alloc] init];
    [NSThread sleepForTimeInterval:3];
    NSLog(@"pthread_once 执行内部 ====== %@",NSThread.currentThread);
}

- (void)pthread_once_Method
{
    pthread_once_t once = PTHREAD_ONCE_INIT;
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(3, queue, ^(size_t index) {
        NSLog(@"开始执行：%zu ====== %@",index,NSThread.currentThread);
        int onceValue = pthread_once(&once, &pthread_once_Function);
        NSLog(@"结束执行：%zu ====== %d",index,onceValue);
    });
}

#pragma mark - 属性（）

/* 
 atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。
 
 @property (atomic ,copy) NSString *testStrig;

 设置成员变量的@property属性时，默认为atomic，提供多线程安全。

 atomic 修饰的对象，系统会保证在其自动生成的 getter/setter 方法中的操作是完整的，不受其他线程的影响。例如 A 线程在执行 getter 方法时，B线程执行了 setter 方法，此时 A 线程依然会得到一个完整无损的对象。
 
 - (void)setTestStrig:(NSString *)testStrig
 {
     {lock}
     if (![_testStrig isEqualToString:testStrig])
     {
        _testStrig = testStrig;
     }
    {unlock}
 }

 
atomic
 默认修饰符
 
 会保证CPU能在别的线程访问这个属性之前先执行完当前操作
 
 读写速度慢
 
 线程不安全 - 如果有另一个线程 D 同时在调[name release]，那可能就会crash，因为 release 不受 getter/setter 操作的限制。也就是说，这个属性只能说是读/写安全的，但并不是线程安全的，因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。
 
 nonatomic
 
 不默认
 
 速度更快，提高性能
 
 线程不安全
 
 如果两个线程同时访问会出现不可预料的结果
 */

#pragma mark - setter and getter

- (NSArray<NSString *> *)titleArray
{
    if (_titleArray == nil)
    {
        _titleArray = @[@"测试 nonatomic",
                        @"synchronized（互斥锁）",@"NSLock（互斥锁）",@"pthread_mutex（互斥锁）",
                        @"NSLock(递归死锁)",@"NSRecursiveLock(递归锁)",@"pthread_mutex（递归锁）",
                        @"NSConditionLock（条件锁）",@"NSCondition（条件锁）",@"pthread_mutex（条件锁）",
                        @"dispatch_semaphore（信号量）",@"pthread_mutex（信号量）",
                        @"dispatch_barrier(读写锁)",@"pthread_rwlock_t(读写锁)",
                        @"dispatch_once_t",@"pthread_once_Method"];
    }
    return _titleArray;
}

@end
